﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace Jerry.DroidBuilder.ThemeEditor
{
    public class NinePatchDecoder
    {
        private static readonly int NP_CHUNK_TYPE = 0x6E705463;

        public static NinePatchInfo Decode(string fileName, string targetFileName)
        {
            using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                var originalImage = Image.FromStream(stream);
                var imageWidth = originalImage.Width;
                var imageHeight = originalImage.Height;
                var info = GetNinePatch(stream);

                info.ImageWidth = imageWidth;
                info.ImageHeight = imageHeight;

                if (null != targetFileName)
                {
                    var bitmap = new Bitmap(imageWidth + 2, imageHeight + 2);

                    using (var graphic = Graphics.FromImage(bitmap))
                    {
                        // Draws content areas.
                        graphic.FillRectangle(Brushes.Black, info.PaddingLeft + 1, imageHeight + 1, imageWidth - info.PaddingRight - info.PaddingLeft, 1);
                        graphic.FillRectangle(Brushes.Black, imageWidth + 1, info.PaddingTop + 1, 1, imageHeight - info.PaddingBottom - info.PaddingTop);

                        // Draws horizontal stretchable areas.
                        var divs = info.HorizontalStretchableAreas;
                        for (int k = 0; k < divs.Length; k += 2)
                        {
                            graphic.FillRectangle(Brushes.Black, divs[k] + 1, 0, divs[(k + 1)] - divs[k], 1);
                        }

                        // Draws vertical stretchable areas.
                        divs = info.VerticalStretchableAreas;
                        for (int m = 0; m < divs.Length; m += 2)
                        {
                            graphic.FillRectangle(Brushes.Black, 0, divs[m] + 1, 1, divs[(m + 1)] - divs[m]);
                        }

                        // Draws the original image.
                        graphic.DrawImage(originalImage, 1, 1, imageWidth, imageHeight);
                    }

                    bitmap.Save(targetFileName, ImageFormat.Png);
                }

                return info;
            }
        }

        private static NinePatchInfo GetNinePatch(Stream stream)
        {
            stream.Seek(8, SeekOrigin.Begin);

            while (true)
            {
                var i = stream.ReadInt32();

                if (stream.ReadInt32() == NP_CHUNK_TYPE)
                {
                    break;
                }

                stream.SkipBytes(i + 4);
            }

            return CreateInfo(stream);
        }

        private static NinePatchInfo CreateInfo(Stream stream)
        {
            stream.SkipBytes(1);

            int horizontalAreaCount = stream.ReadByte();
            int verticalAreaCount = stream.ReadByte();

            stream.SkipBytes(1);
            stream.SkipBytes(8);

            int padLeft = stream.ReadInt32();
            int padRight = stream.ReadInt32();
            int padTop = stream.ReadInt32();
            int padBottom = stream.ReadInt32();

            stream.SkipBytes(4);

            int[] horizontalAreas = stream.ReadInt32Array(horizontalAreaCount);
            int[] verticalAreas = stream.ReadInt32Array(verticalAreaCount);

            return new NinePatchInfo(padLeft, padRight, padTop, padBottom, horizontalAreas, verticalAreas);
        }
    }

    public static class NinePatchDecoderExtensionMethods
    {
        public static int ReadInt32(this Stream stream)
        {
            return (stream.ReadByte() << 24) + (stream.ReadByte() << 16) + (stream.ReadByte() << 8) + stream.ReadByte();
        }

        public static void SkipBytes(this Stream stream, int count)
        {
            stream.Position += count;
        }

        public static int[] ReadInt32Array(this Stream stream, int count)
        {
            int[] array = new int[count];

            for (int i = 0; i < count; i++)
            {
                array[i] = stream.ReadInt32();
            }

            return array;
        }
    }
}
